package com.hy.video.monitor.gb28181.transmit.cmd;

import java.text.ParseException;
import java.util.ArrayList;

import javax.sip.Dialog;
import javax.sip.InvalidArgumentException;
import javax.sip.PeerUnavailableException;
import javax.sip.SipFactory;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.header.*;
import javax.sip.message.Request;

import com.hy.video.monitor.common.StreamInfo;
import com.hy.video.monitor.gb28181.session.VideoStreamSessionManager;
import com.hy.video.monitor.storager.IRedisCatchStorage;
import com.hy.video.monitor.conf.SipConfig;
import com.hy.video.monitor.gb28181.bean.Device;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @description:摄像头命令request创造器 TODO 冗余代码太多待优化
 * @author: KangJonney
 * @date: 2020年5月6日 上午9:29:02
 */

/**
 * @Classname SIPRequestHeaderProvider
 * @Description TODO IPC命令request创造器：包括 Request 接口常数中的方法：ACK、BYE、CANCEL、INVITE、OPTIONS、REGISTER、
 * NOTIFY、SUBSCRIBE、MESSAGE、REFER、INFO、PRACK 和 UPDATE。
 * @Date 2022/3/11 10:27
 * @Created by KangJonney
 * INVITE：用于发起呼叫请求
 * ACK：对INVITE请求的响应的确认
 * OPTIONS：用于询问被叫端的能力信息，但OPTIONS 本身并不能发起呼叫
 * REGISTER：用于用户向SIP服务器传送位置信息或地址信息
 * CANCEL：取消当前的请求，但它并不能中止已经建立的连接
 * BYE: 当一个用户决定中止会话时，可以使用BYE 来结束会话
 * NOTIFY：发送一个通知事件
 * MESSAGE：承担设备控制命令
 * INFO: Info方法的消息体携带视音频回放控制命令
 * SUBSCRIBE: 签订一个通知事件
 * PUBLISH: 发送事件状态给状态服务器
 * UPDATE：在建立呼叫阶段修改会话属性
 * PRACK：标识临时响应的可靠性
 * <p>
 * 状态类型：
 * 1xx：临时消息：表示请求消息已经收到，后面将继续处理该请求。
 * 2xx：成功消息：表示请求已经被成功的理解、接受或执行。
 * 3xx：重定向消息：表示为了完成请求还需采取更进一步的动作。
 * 4xx：客户机错误：表示该请求含有语法错误或在这个服务器上不能被满足。
 * 5xx：服务器错误：表示该服务器不能处理一个明显有效的请求。
 * 6xx：全局性故障：表示该请求在任何服务器上都不能被实现。
 */
@Component
public class SIPRequestHeaderProvider {

    @Autowired
    private SipConfig sipConfig;

    @Autowired
    private SipFactory sipFactory;

    @Autowired
    private IRedisCatchStorage redisCatchStorage;

    @Autowired
    private VideoStreamSessionManager streamSession;


    /**
     * 构造 MESSAGE
     *
     * @param device
     * @param content
     * @param viaTag
     * @param toTag
     * @param callIdHeader
     * @return
     * @throws PeerUnavailableException
     */
    public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
        Request request = null;
        // Rquest-Line: REGISTER sip:SIP服务器编码@目的域名或IP地址端口 SIP/2.0
        SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
        // Via: SIP/2.0/UDP 源域名或IP地址端口
        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
        ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
        viaHeader.setRPort();
        viaHeaders.add(viaHeader);
        // From: <sip:SIP设备编码@源域名>;tag=185326220
        SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),
                sipConfig.getIp() + ":" + sipConfig.getPort());
        Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
        FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
        // To: <sip:SIP设备编码@源域名>
        SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getDomain());
        Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
        ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);

        // Max-Forwards: 70
        MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
        // CSeq: 1 MESSAGE
        CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE);

        request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
                toHeader, viaHeaders, maxForwards);
        // Content-Type: MANSCDP+xml
        ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
        request.setContent(content, contentTypeHeader);
        return request;
    }

    /**
     * 构造 INVITE 用于发起呼叫请求
     *
     * @param device
     * @param channelId
     * @param content
     * @param viaTag
     * @param fromTag
     * @param toTag
     * @param ssrc
     * @param callIdHeader
     * @return
     * @throws ParseException
     * @throws InvalidArgumentException
     * @throws PeerUnavailableException
     */
    public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, String ssrc, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
        Request request = null;
        //请求行 Rquest-Line: REGISTER sip:SIP服务器编码@目的域名或IP地址端口 SIP/2.0
        SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
        // Via: SIP/2.0/UDP 源域名或IP地址端口
        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
        ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
        viaHeader.setRPort();
        viaHeaders.add(viaHeader);

        // From: <sip:SIP设备编码@源域名>;tag=185326220
        SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
        Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
        FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记，否则无法创建会话，无法回应ack
        // To: <sip:SIP设备编码@源域名>
        SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, sipConfig.getDomain());
        Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
        ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, null);

        // Max-Forwards: 70
        MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);

        // CSeq: 1 INVITE
        CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(Request.INVITE), Request.INVITE);
        request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards);

        Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp() + ":" + sipConfig.getPort()));
        // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory()
        // .createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort()));
        // Contact: <sip:SIP设备编码@源IP地址端口>
        request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
        // Subject:媒体流发送者设备编码:发送端媒体流序列号,媒体流接收者设备编码:接收端媒体流序列号
        SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
        request.addHeader(subjectHeader);
        // Content-Type: Application/SDP
        ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
        request.setContent(content, contentTypeHeader);
        return request;
    }

    /**
     * 构造 Playback 历史视频回放 INVITE
     *
     * @return
     */
    public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader, String ssrc) throws ParseException, InvalidArgumentException, PeerUnavailableException {
        Request request = null;
        //请求行
        SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
        // via
        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
        ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
        viaHeader.setRPort();
        viaHeaders.add(viaHeader);
        //from
        SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
        Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
        FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记，否则无法创建会话，无法回应ack
        //to
        SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, sipConfig.getDomain());
        Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
        ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, null);

        //Forwards
        MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);

        //ceq
        CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(Request.INVITE), Request.INVITE);
        request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards);

        Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp() + ":" + sipConfig.getPort()));
        // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort()));
        request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
        // Subject
        SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
        request.addHeader(subjectHeader);

        ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
        request.setContent(content, contentTypeHeader);
        return request;
    }

    /**
     * 构造 BYE 当一个用户决定中止会话时，可以使用BYE 来结束会话
     *
     * @param device
     * @param channelId
     * @param viaTag
     * @param fromTag
     * @param toTag
     * @param callId
     * @return
     * @throws ParseException
     * @throws InvalidArgumentException
     * @throws PeerUnavailableException
     */
    public Request createByteRequest(Device device, String channelId, String viaTag, String fromTag, String toTag, String callId) throws ParseException, InvalidArgumentException, PeerUnavailableException {
        Request request = null;
        //请求行
        SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
        // via
        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
        ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
        viaHeaders.add(viaHeader);
        //from
        SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
        Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
        FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记，否则无法创建会话，无法回应ack
        //to
        SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, sipConfig.getDomain());
        Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
        ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);

        //Forwards
        MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);

        //ceq
        CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.BYE);
        CallIdHeader callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(callId);
        request = sipFactory.createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards);

        Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp() + ":" + sipConfig.getPort()));

        return request;
    }


    /**
     * 构造 SUBSCRIBE: 签订一个通知事件
     *
     * @param device
     * @param content      订阅通知命令消息体
     * @param viaTag
     * @param fromTag
     * @param toTag
     * @param expires      Expires 是说明本次注册的有效期，单位是秒。 SIP代理向SIP服务器发送 Register请求,Expires字段的值为0,表示SIP代理要注销;
     * @param event        事件类型
     * @param callIdHeader
     * @return
     * @throws ParseException
     * @throws InvalidArgumentException
     * @throws PeerUnavailableException
     */
    public Request createSubscribeRequest(Device device, String content, String viaTag, String fromTag, String toTag, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
        Request request = null;
        // sipuri
        SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
        // via
        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
        ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(),
                device.getTransport(), viaTag);
        viaHeader.setRPort();
        viaHeaders.add(viaHeader);
        // from
        SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),
                sipConfig.getIp() + ":" + sipConfig.getPort());
        Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
        FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
        // to
        SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getDomain());
        Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
        ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);

        // Forwards
        MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);

        // ceq
        CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(Request.SUBSCRIBE), Request.SUBSCRIBE);

        request = sipFactory.createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader,
                toHeader, viaHeaders, maxForwards);


        Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp() + ":" + sipConfig.getPort()));
        request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));

        // Expires
        ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires);
        request.addHeader(expireHeader);

        // Event
        EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader(event);
        request.addHeader(eventHeader);

        ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
        request.setContent(content, contentTypeHeader);
        return request;
    }

    /**
     * 构造INFO: 方法的消息体携带视音频回放控制命令
     *
     * @param device
     * @param streamInfo
     * @param content
     * @param cseq
     * @return
     * @throws PeerUnavailableException
     * @throws ParseException
     * @throws InvalidArgumentException
     */
    public Request createInfoRequest(Device device, StreamInfo streamInfo, String content, Long cseq)
            throws PeerUnavailableException, ParseException, InvalidArgumentException {
        Request request = null;
        if (streamInfo == null) return null;
        Dialog dialog = streamSession.getDialogByStream(streamInfo.getDeviceID(), streamInfo.getChannelId(), streamInfo.getStream());

        SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(),
                device.getHostAddress());
        // via
        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
        ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(),
                device.getTransport(), null);
        viaHeader.setRPort();
        viaHeaders.add(viaHeader);
        // From: <sip:SIP设备编码@源域名>;tag=185326220
        // From: <sip:120105110228023020@192.168.40.55:7100>;tag=4105413651
        SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),
                sipConfig.getDomain());
        Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
        FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, dialog.getLocalTag());
        // To: <sip:SIP设备编码@源域名>
        // To: <sip:00000000001310018021@192.168.40.66:7100>;tag=4232579586
        SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(streamInfo.getChannelId(),
                sipConfig.getDomain());
        Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
        ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, dialog.getRemoteTag());

        // Call-ID: 2259139548
        CallIdHeader callIdHeader = dialog.getCallId();

        // Max-Forwards: 70
        MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
        if (cseq == null) {
            cseq = redisCatchStorage.getCSEQ(Request.INFO);
        }
        // CSeq: 1 INFO
        CSeqHeader cSeqHeader = sipFactory.createHeaderFactory()
                .createCSeqHeader(cseq, Request.INFO);
        // 组装头域
        request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,
                fromHeader, toHeader, viaHeaders, maxForwards);
        // Contact: <sip:SIP设备编码@源IP地址端口>
        Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory()
                .createSipURI(sipConfig.getId(), sipConfig.getIp() + ":" + sipConfig.getPort()));
        request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));

        ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application",
                "MANSRTSP");
        request.setContent(content, contentTypeHeader);
        return request;
    }
}

